package com.yc.spring;

import com.yc.spring.bank.bean.Record;
import com.yc.spring.bmi.Demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;
import java.util.stream.Stream;

public class MyAnnotationConfigApplicationContext {


    private HashMap<String, Object> ioc = new HashMap<>();

    private MyAnnotationConfigApplicationContext(Class cls) throws InstantiationException, IllegalAccessException {
        // 读取配置类注解
        Configuration configuration = (Configuration) cls.getAnnotation(Configuration.class);
        if (configuration == null) {
            throw new RuntimeException("该类不是Spring配置类");
        }
        ComponentScan componentScan = (ComponentScan) cls.getAnnotation(ComponentScan.class);
        if (componentScan != null) {
            // 读取包扫描路径
            String[] values = componentScan.value();
            // 包扫描 ==> 组件 => new => 创建 bean ==> 加入IOC容器
            // 1. 开发模式
  //          packageScan(values[0]);
            // 2. jar模式 ==> jar == zip ==> javax.zip

            packageBeanScan(values[0]);
        }

        Field[] fields = cls.getDeclaredFields();
        for (Field field : fields) {
            if (field.getAnnotation(Resource.class) != null ||
                    field.getAnnotation(Autowired.class) != null){
                String name = "";
                if (field.getAnnotation(Resource.class) != null) {
                    System.out.println(field.getName() + "属性");
                    name = field.getName();
                    Class<?> type = field.getType();
                    Object o = type.newInstance();
                    System.out.println(field.getType() + "===========");
                    ioc.put(name,o);
                    }
                }
                if (field.getAnnotation(Autowired.class) != null) {
                    String name = "";
                    name = field.getName();
                    Class<?> type = field.getType();
                    Object o = type.newInstance();
                    System.out.println(field.getName() + "属性");
                    ioc.put(name,type);
//                    ioc.put(name, a);
                }
            }
    }

    // 包扫描 value => com.yc.spring
    private void packageScan(String _package) {
        // 获取类加载器
        ClassLoader classLoader = getClass().getClassLoader();
        URL url = classLoader.getResource(_package.replaceAll("\\.", "/"));
        // url ==> http:// file:// ftp://
        String path = url.getPath();

        // 廖老师的补丁
        path = path.replace("%e5%bf%b5%e6%b5%81%e5%b9%b4", "念流年");
        File dir = new File(path);
        System.out.println(dir);
        // com.yc.spring.bean.Record
        // ==> F:...
        // 使用递归扫描
        // 1.定义一个递归方法
        // 2. 参数一: 要扫描的路径，参数二: 手机文件集合
        // 3. 递归的退出
        List<File> fileList = new ArrayList<>();
        scanClassFile(dir, fileList);
        // 将文件路径 ==> 类路径
        // 读取类上的注解，判断是否是组件
        // 如果是 组件 那么，创建并加入到 ioc 容器中
        fileList.forEach(f -> {
            System.out.println(f);
            // 将文件路径 ==> 类路径
            String classpath = f.getAbsolutePath().
                    replaceAll(".+\\\\classes\\\\(.+)\\.class", "$1");
            classpath = classpath.replaceAll("\\\\", ".");
            System.out.println(classpath + "++++++++++++");
            // 读取类上的注解，判断是否是组件
            try {
                Class cls = Class.forName(classpath);
                if (cls.getAnnotation(Component.class) != null ||
                        cls.getAnnotation(Controller.class) != null ||
                        cls.getAnnotation(Service.class) != null ||
                        cls.getAnnotation(Repository.class) != null) {
                    // 创建对象
                    Object bean = cls.newInstance();
                    String name = "";
                    if (cls.getAnnotation(Component.class) != null) {
                        Component c = (Component) cls.getAnnotation(Component.class);
                        name = c.value();
                    }
                    if (cls.getAnnotation(Controller.class) != null) {
                        Controller c = (Controller) cls.getAnnotation(Controller.class);
                        name = c.value();
                    }
                    if (cls.getAnnotation(Service.class) != null) {
                        Service c = (Service) cls.getAnnotation(Service.class);
                        name = c.value();
                    }
                    if (cls.getAnnotation(Repository.class) != null) {
                        Repository c = (Repository) cls.getAnnotation(Repository.class);
                        name = c.value();
                    }

                    // 如果是 组件 那么，创建并加入到 ioc 容器中
                    if (name.isEmpty()) {
                        // 未配置了名字
                        name = cls.getSimpleName();
                        // 默认名是类名首字母小写
                        name = name.substring(0, 1).toLowerCase() + name.substring(1);
                    }
                    // 添加到 IOC 容器中 ==> Map
                    ioc.put(name, bean);
                }
            } catch (ClassNotFoundException e) {
                throw new RuntimeException("类未找到", e);
            } catch (InstantiationException e) {
                throw new RuntimeException("构造函数内部异常", e);
            } catch (IllegalAccessException e) {
                throw new RuntimeException("访问异常，private构造方法...", e);
            }


        });


    }

    // 包扫描 value => com.yc.spring
    private void packageBeanScan(String _package) {
        // 获取类加载器
        ClassLoader classLoader = getClass().getClassLoader();
        URL url = classLoader.getResource(_package.replaceAll("\\.", "/"));
        // url ==> http:// file:// ftp://
        String path = url.getPath();

        // 廖老师的补丁
        path = path.replace("%e5%bf%b5%e6%b5%81%e5%b9%b4", "念流年");
        File dir = new File(path);
        System.out.println(dir);
        // com.yc.spring.bean.Record
        // ==> F:...
        // 使用递归扫描
        // 1.定义一个递归方法
        // 2. 参数一: 要扫描的路径，参数二: 手机文件集合
        // 3. 递归的退出
        List<File> fileList = new ArrayList<>();
        scanClassFile(dir, fileList);
        // 将文件路径 ==> 类路径
        // 读取类上的注解，判断是否是组件
        // 如果是 组件 那么，创建并加入到 ioc 容器中
        fileList.forEach(f -> {
            System.out.println(f);
            // 将文件路径 ==> 类路径
            String classpath = f.getAbsolutePath().
                    replaceAll(".+\\\\classes\\\\(.+)\\.class", "$1");
            classpath = classpath.replaceAll("\\\\", ".");

            // 读取方法上的注解，判断是否含有@Bean
            try {
                Class cls = Class.forName(classpath);
                Method[] methods = cls.getDeclaredMethods();
                for (Method method : methods) {
                    if (method.getAnnotation(Bean.class) != null) {
                        System.out.println(method.getName() + "方法");
                        System.out.println(method.getReturnType() + "返回");
                        String name = "";
                        Bean c = (Bean) method.getAnnotation(Bean.class);
                        for (String s : c.value()) {
                            if (s != null && !s.isEmpty()) {
                                if(ioc.keySet().contains(s)){
                                    throw new RuntimeException("Bean设置重名");
                                }
                                name = s;
                                Object o = method.invoke(cls.newInstance());
                                ioc.put(name, o);
                            } else{
                                System.out.println(name + "    ****有名字");
                                // 未配置了名字
                                name = cls.getSimpleName();
                                // 默认名是类名首字母小写
                                name = method.getReturnType().getSimpleName();
                                // name.substring(0, 1).toLowerCase() + name.substring(1)
                                System.out.println(name + "默认名字");
                                Object o = method.invoke(cls.newInstance());
                                ioc.put(name, o);
                            }
                        }
                    }
                }
            } catch (ClassNotFoundException e) {
                throw new RuntimeException("类未找到", e);
            } catch (InstantiationException e) {
                throw new RuntimeException("构造函数内部异常", e);
            } catch (IllegalAccessException e) {
                throw new RuntimeException("访问异常，private构造方法...", e);
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }

        });
    }

    private void scanClassFile(File dir, List<File> fileList) {
        for (File file : dir.listFiles()) {
            if (file.isFile()) {
                fileList.add(file);
            } else if (file.isDirectory()) {
                scanClassFile(file, fileList);
            }
        }

    }

    public <T> T getBean(Class<T> cls) {
        // 获取 map 集合的值集合
        // ioc.keySet(); 键集合 ==> bean 的 id
        Collection<Object> values = ioc.values(); // 值集合 bean
        Object[] beans = values.stream()
                .filter(b -> b.getClass().equals(cls))
                .toArray();
        if (beans.length == 0) {
            throw new RuntimeException("容器中没有该bean :" + cls);
        } else if (beans.length == 1) {
            return (T) beans[0];
        } else {
            // 过滤出 主 bean
            beans = Stream.of(beans)
                    .filter(b -> b.getClass().getAnnotation(Primary.class) != null)
                    .toArray();
            if (beans.length == 0) {
                throw new RuntimeException("容器中配置了多个该类型的 bean, 但是没有配置主bean :" + cls);
            } else if (beans.length == 1) {
                return (T) beans[0];
            } else {
                throw new RuntimeException("容器中配置了多个该类型的 bean, 但是配置了多个主bean :" + cls);
            }
        }
    }

    public <T> T getBean(String id) {
        Object o = ioc.get(id);
        if (o == null) {
            throw new RuntimeException("容器中没有该bean :" + id);
        }
        return (T) o;
    }

    public static void main(String[] args) throws InstantiationException, IllegalAccessException {
//        MyAnnotationConfigApplicationContext cxt = new MyAnnotationConfigApplicationContext(BeanConfig.class);
//        BeanConfig bean = cxt.getBean(board);
//        System.out.println(bean);
        MyAnnotationConfigApplicationContext cxt = new MyAnnotationConfigApplicationContext(Demo.class);
        Demo bean = cxt.getBean(Demo.class);
        Record record = bean.record;
        System.out.println(record + "=========");
//        Collection<Object> values = cxt.ioc.values();
//        for (Object value : values) {
//            for (Field field : value.getClass().getFields()) {
//                System.out.println(field + "asedr");
//            }
//            System.out.println(value.getClass().getFields() + "+++");
//        }
//        BmiMeasurer b = cxt.getBean("a");
//        //cxt.getBean(BmiMeasurer.class);
//        System.out.println(b  + " 打印" );
        System.out.println(  "容器对象个数" +  cxt.ioc.size());
    }
}
